﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading;
using System.Xml.Linq;
using RoMBot.Classes;
using RoMBot.MemoryLib;
using RoMBot.Properties;

namespace RoMBot.Database
{
    public class Skills : Dictionary<string, Skill>
    {
        public Skills()
        {
            var skills = XDocument.Parse( Resources.skills );
            //XDocument skills_local = XDocument.Load( @"Database\skills_local.xml" );
            var skillsElement = skills.Element( "skills" );
            if ( skillsElement == null )
            {
                return;
            }
            var x = skillsElement.Elements( "skill" ).Select( s => new Skill
                                                                   {
                                                                       ID = s.Attribute( "name" ).Value,
                                                                       Id = ( s.Attribute( "id" ) != null && !string.IsNullOrWhiteSpace( s.Attribute( "id" ).Value ) ) ? Convert.ToInt32( s.Attribute( "id" ).Value ) : -1,
                                                                       TargetType = s.Attribute( "target" ) != null ? GetTargetType( s.Attribute( "target" ).Value ) : SkillTargetType.None,
                                                                       Type = s.Attribute( "type" ) != null ? GetSkillType( s.Attribute( "type" ).Value ) : SkillType.Damage,
                                                                       CastTime = s.Attribute( "casttime" ) != null ? Convert.ToSingle( s.Attribute( "casttime" ).Value ) : 0,
                                                                       BuffName = s.Attribute( "buffname" ) != null ? s.Attribute( "buffname" ).Value : "",
                                                                       ReqBuffCount = s.Attribute( "reqbuffcount" ) != null ? Convert.ToInt32( s.Attribute( "reqbuffcount" ).Value ) : 0,
                                                                       ReqBuffTarget = s.Attribute( "reqbufftarget" ) != null ? GetRequiredTargetType( s.Attribute( "reqbufftarget" ).Value.ToLower() ) : RequiredTargetType.None,
                                                                       ReqBuffName = s.Attribute( "reqbuffname" ) != null ? s.Attribute( "reqbuffname" ).Value : "",
                                                                       NoBuffCount = s.Attribute( "nobuffcount" ) != null ? Convert.ToInt32( s.Attribute( "nobuffcount" ).Value ) : 0,
                                                                       NoBuffTarget = s.Attribute( "nobufftarget" ) != null ? GetRequiredTargetType( s.Attribute( "nobufftarget" ).Value.ToLower() ) : RequiredTargetType.None,
                                                                       NoBuffName = s.Attribute( "nobuffname" ) != null ? s.Attribute( "nobuffname" ).Value : "",
                                                                       MaxHpPer = s.Attribute( "hpper" ) != null ? Convert.ToInt32( s.Attribute( "hpper" ).Value ) : 0,
                                                                       TargetMaxHpPer = s.Attribute( "targethpper" ) != null ? Convert.ToInt32( s.Attribute( "targethpper" ).Value ) : 0,
                                                                       InBattle = s.Attribute( "inbattle" ) != null ? Convert.ToBoolean( s.Attribute( "inbattle" ).Value ) : ( bool? ) null,
                                                                       AoECenter = s.Attribute( "aoecenter" ) != null ? GetAoECenter( s.Attribute( "aoecenter" ).Value ) : AoECenter.Player,
                                                                       AoERange = s.Attribute( "aoerange" ) != null ? Convert.ToDouble( s.Attribute( "aoerange" ).Value ) : 0d,
                                                                       Nature = s.Attribute( "nature" ) != null ? Convert.ToInt32( s.Attribute( "nature" ).Value ) : 0,
                                                                       Psi = s.Attribute( "psi" ) != null ? Convert.ToInt32( s.Attribute( "psi" ).Value ) : 0,
                                                                       Range = s.Attribute( "range" ) != null ? Convert.ToInt32( s.Attribute( "range" ).Value ) : -1,
                                                                       MinRange = s.Attribute( "minrange" ) != null ? Convert.ToInt32( s.Attribute( "minrange" ).Value ) : 0,
                                                                       MaxUse = s.Attribute( "maxUse" ) != null ? Convert.ToInt32( s.Attribute( "maxUse" ).Value ) : 0,
                                                                       GlobalCooldown = s.Attribute( "globalcooldown" ) != null && Convert.ToBoolean( s.Attribute( "globalcooldown" ).Value )
                                                                   } );
            foreach ( var item in x.Where( item => !this.ContainsKey( item.ID ) ) )
            {
                this.Add( item.ID, item );
            }
        }

        public static void ProcessPlayerSkills()
        {
            Bot.Player.Skills.Clear();
            var proc = Bot.Instancia.ProcId;
            const int skillsTableTabSize = 0x10;
            const int skillSize = 0x4c;

            var profileSkills = Bot.Player.Profile.Skills.Values.Where( s => !string.IsNullOrWhiteSpace( s.ID ) ).ToList();

            for ( var tab = 1; tab < 6; tab++ )
            {
                var tabBaseAddress = Memory.ReadRepeat<uint>( proc, Addresses.skillsTableBase + skillsTableTabSize * ( tab - 1 ) + Addresses.skillsTableTabStartAddress_offset );
                var tabEndAddress = Memory.ReadRepeat<uint>( proc, Addresses.skillsTableBase + skillsTableTabSize * ( tab - 1 ) + Addresses.skillsTableTabEndAddress_offset );
                if ( tabBaseAddress == 0 || tabEndAddress == 0 )
                {
                    continue;
                }
                var tabSkills = Memory.ReadObjectArray<TabSkill>( ( int ) tabBaseAddress, ( int ) ( ( tabEndAddress - tabBaseAddress ) / skillSize ) );
                if ( tabSkills == null || tabSkills.Length == 0 )
                {
                    continue;
                }
                for ( var i = 0; i < tabSkills.Length; i++ )
                {
                    var skilladdress = tabBaseAddress + ( i ) * skillSize;
                    var tabSkill = tabSkills[ i ];
                    if ( tabSkill.Id <= 0 )
                    {
                        continue;
                    }
                    var profSkill = profileSkills.FirstOrDefault( s => s.Id == tabSkill.Id );
                    if ( profSkill == null )
                    {
                        continue;
                    }
                    var dbSkill = Bot.Instance.Skills.ContainsKey( profSkill.ID ) ? Bot.Instance.Skills[ profSkill.ID ] : null;
                    if ( dbSkill == null )
                    {
                        continue;
                    }
                    profSkill.Address = ( int ) skilladdress;
                    profSkill.Update( tabSkill );
                    if ( string.IsNullOrWhiteSpace( profSkill.Name ) )
                    {
                        continue;
                    }
                    profSkill.MaxHpPer = profSkill.MaxHpPer != 0 ? profSkill.MaxHpPer : 100;
                    profSkill.TargetMaxHpPer = profSkill.TargetMaxHpPer != 0 ? profSkill.TargetMaxHpPer : 100;
                    Bot.Instance.Skills[ profSkill.ID ].SkillTab = tab;
                    Bot.Instance.Skills[ profSkill.ID ].SkillNum = i + 1;
                    profSkill.SkillTab = tab;
                    profSkill.SkillNum = i + 1;
                    profSkill.Available = profSkill.Aslevel <= Bot.Player.Level;
                    profSkill.ReqBuffCount = profSkill.ReqBuffCount != 0 ? profSkill.ReqBuffCount : dbSkill.ReqBuffCount;
                    profSkill.RebuffCut = profSkill.RebuffCut > 0 ? profSkill.RebuffCut : 0;
                    Bot.Player.Skills.Add( profSkill );
                    Bot.Player.Profile.Skills[ profSkill.ID ] = profSkill;
                }
            }
            Bot.Player.Skills = Bot.Player.Skills.OrderByDescending( s => s.Priority ).ToList();
        }

        public static void UpdateProfileSkill( Skill profileSkill )
        {
            try
            {
                Monitor.Enter( Bot.Player.Skills );
                if ( profileSkill == null )
                {
                    return;
                }
                var current = Bot.Player.Skills.SingleOrDefault( s => s.Id == profileSkill.Id );
                if ( current == null )
                {
                    return;
                }
                current.MaxHpPer = profileSkill.MaxHpPer != 0 ? profileSkill.MaxHpPer : 100;
                current.TargetMaxHpPer = profileSkill.TargetMaxHpPer != 0 ? profileSkill.TargetMaxHpPer : 100;
                current.Available = profileSkill.Aslevel <= Bot.Player.Level;
                current.ReqBuffCount = profileSkill.ReqBuffCount != 0 ? profileSkill.ReqBuffCount : current.ReqBuffCount;
                current.RebuffCut = profileSkill.RebuffCut > 0 ? profileSkill.RebuffCut : 0;
                current.AutoUse = profileSkill.AutoUse;
                current.InBattle = profileSkill.InBattle;
                current.HotKey = profileSkill.HotKey;
                current.Modifier = profileSkill.Modifier;
                current.Priority = profileSkill.Priority;
                current.PullOnly = profileSkill.PullOnly;
            }
            finally
            {
                Monitor.Exit( Bot.Player.Skills );
            }
        }

        private static SkillType GetSkillType( string skillType )
        {
            switch ( skillType.ToLower() )
            {
                case "damage":
                    return SkillType.Damage;

                case "buff":
                    return SkillType.Buff;

                case "debuff":
                    return SkillType.Debuff;

                case "dot":
                    return SkillType.DoT;

                case "heal":
                    return SkillType.Heal;

                case "hot":
                    return SkillType.HoT;

                case "summon":
                    return SkillType.Summon;
            }
            return SkillType.Damage;
        }

        private static SkillTargetType GetTargetType( string targetType )
        {
            switch ( targetType.ToLower() )
            {
                case "self":
                    return SkillTargetType.Self;
                case "friendly":
                case "player":
                    return SkillTargetType.Friendly;
                case "enemy":
                case "target":
                    return SkillTargetType.Enemy;
            }
            return SkillTargetType.Self;
        }

        private static RequiredTargetType GetRequiredTargetType( string targetType )
        {
            switch ( targetType.ToLower() )
            {
                case "player":
                    return RequiredTargetType.Player;
                case "target":
                    return RequiredTargetType.Target;
            }
            return RequiredTargetType.None;
        }


        private static AoECenter GetAoECenter( string targetType )
        {
            switch ( targetType.ToLower() )
            {
                case "player":
                    return AoECenter.Player;
                case "target":
                    return AoECenter.Target;
            }
            return AoECenter.Player;
        }

    }
}